/**
 * doio 框架抽离出来的全局日志组件，要配合doio使用，
 * 并且doio运行服务要使用daemon接口，也就是要使用cluster。
 * 如果不使用，而仅仅是一个进程处理请求，则仅仅是输出日志。
 */

const fs = require('fs');
const cluster = require('cluster');
const http2 = require('http2');

/**
 * 
 * @param {object} options 
 * 
 */

var logger = function (options = {}) {

  if (!(this instanceof logger)) {
    return new logger(options);
  }

  //在self模式，需要你自己定义执行操作的函数并通过配置传递
  //stdio | file | self
  this.logType = 'stdio';

  this.logFile = '';

  this.logErrorFile = '';

  this.out = null;

  this.stdout = null;
  this.stderr = null;

  this.watchReset = false;

  this.name = 'log';

  this.handle = null;

  if (typeof options === 'object') {
    for (let k in options) {
      switch (k) {
        case 'logType':
          this.logType = options[k]; break;

        case 'logFile':
          this.logFile = options[k]; break;

        case 'logErrorFile':
          this.logErrorFile = options[k]; break;

        case 'handle':
          if (typeof options[k] === 'function') {
            this.handle = options[k];
          }
          break;
        
          default: ;
      }
    }
  }

};

logger.prototype.watch = function () {

  if (this.logType !== 'file') {
    return;
  }

  let wtf = (curr, prev) => {
    if (curr.nlink == 0 && prev.nlink > 0) {
      if (this.watchReset) {
        return;
      }
      this.watchReset = true;
      this.destroy();
      this.initLogFile();
      this.watchReset = false;
    }
  };

  fs.watchFile(this.logFile, wtf);
  fs.watchFile(this.logErrorFile, wtf);
};

logger.prototype.destroy = function () {
  try {
    if (this.stderr && !this.stderr.destroyed) {
      this.stderr.destroy();
    }
    if (this.stdout && !this.stdout.destroyed) {
      this.stdout.destroy();
    }
  } catch (err) {

  }

  this.out = this.stdout = this.stderr = null;

};

logger.prototype.initLogFile = function () {
  if (this.logType == 'file') {
    try {
      let ofd = fs.openSync(this.logFile, 'a+');
      let out_log = fs.createWriteStream(this.logFile, {flags: 'a+', fd : ofd});

      let efd = fs.openSync(this.logErrorFile, 'a+');
      let err_log = fs.createWriteStream(this.logErrorFile, {flags: 'a+', fd : efd});

      this.out = new console.Console(out_log, err_log);

      this.stdout = out_log;
      this.stderr = err_log;

    } catch (err) {
      console.error(err);
      this.out = null;
    }
  } else if (this.logType == 'stdio') {
    let opts = {stdout:process.stdout, stderr: process.stderr};
    this.out = new console.Console(opts);
  }

};

/**
 * 
 * @param {*} worker
 * @param {*} msg
 * 要通过doio框架的setMsgEvent挂载到cluster消息事件上
 */
logger.prototype.msgEvent = function () {
  let self = this;
  
  if (typeof this.handle === 'function') {
    return this.handle;
  }

  return (worker, msg) => {
    if (self.out) {
      msg.success ? self.out.log(msg.log) : self.out.error(msg.log);
    }
  };
};

logger.prototype.http1Handle = (req, res, protocol = 'https:') => {

  let req_url = `${protocol}//${req.headers.host}${req.url}`;
  let remote_ip = req.headers['x-real-ip'] || req.socket.remoteAddress;
  let agent = req.headers['user-agent'] || '-';

  res.on('finish', () => {
    let tm = new Date();
    tmstr = `${tm.getFullYear()}-${tm.getMonth()+1}-${tm.getDate()} `
        + `${tm.getHours()}:${tm.getMinutes()}:${tm.getSeconds()}`;

    let log_data = {
      type  : 'log',
      success : true,
      log : `@ ${req.method} | ${req_url} | ${res.statusCode} | ${tmstr} | ${remote_ip} | ${agent}\n`
    };
  
    if (res.statusCode != 200) {
      log_data.success = false;
    }
  
    if (process.send && typeof process.send === 'function') {
      //如果不是开发者实现了send，就是cluster模式
      process.send(log_data);
    } else {
      if (log_data.success) {
        console.log(log_data.log);
      } else {
        console.error(log_data.log);
      }
    }

  });

};

logger.prototype.http2Handle = (stream, headers) => {
  let link = `${headers[':scheme']}://${headers[':authority']}${headers[':path']}`;
  let remote_ip = headers['x-real-ip'] || stream.session.socket.remoteAddress;;
  let agent = headers['user-agent'] || '-';
  let method = headers[':method'];

  stream.on('close', () => {
    if (stream.rstCode !== http2.constants.NGHTTP2_NO_ERROR) {
      return;
    }

    let log_data = {
      type  : 'log',
      success : true,
      log : `@ ${method} | ${link} | ${stream.sentHeaders[':status']} | `
        + `${(new Date()).toLocaleString("zh-Hans-CN")} | ${remote_ip} | ${agent}\n`
    };
  
    if (stream.sentHeaders[':status'] != 200) { log_data.success = false; }

    if (process.send && typeof process.send === 'function') {
      process.send(log_data);
    } else {
      if (log_data.success) {
        console.log(log_data.log);
      } else {
        console.error(log_data.log);
      }
    }
  });

};

logger.prototype.init = function (app) {
  if (cluster.isMaster) {
    this.initLogFile();
    app.setMsgEvent(this.name, this.msgEvent());
    this.watch();
  }

  if (app.config.http2) {
    app.httpServ.handle = this.http2Handle;
  } else {
    app.httpServ.handle = this.http1Handle;
  }
};

module.exports = logger;
